use crate::{AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
use crate::{CrateLint, Resolver, ResolutionError, ScopeSet, Weak};
use crate::{Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
use crate::{is_known_tool, resolve_error};
use crate::ModuleOrUniformRoot;
use crate::Namespace::*;
use crate::build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport};
use crate::resolve_imports::ImportResolver;
use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX};
use rustc::hir::def::{self, DefKind, NonMacroAttrKind};
use rustc::hir::map::{self, DefCollector};
use rustc::{ty, lint};
use rustc::{bug, span_bug};
use syntax::ast::{self, Ident};
use syntax::attr;
use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, Determinacy};
use syntax::ext::base::{MacroKind, SyntaxExtension};
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
use syntax::ext::hygiene::{self, Mark};
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{
    feature_err, is_builtin_attr_name, AttributeGate, GateIssue, Stability, BUILTIN_ATTRIBUTES,
};
use syntax::symbol::{Symbol, keywords};
use syntax::visit::Visitor;
use syntax::util::lev_distance::find_best_match_for_name;
use syntax_pos::{Span, DUMMY_SP};
use errors::Applicability;

use std::cell::Cell;
use std::{mem, ptr};
use rustc_data_structures::sync::Lrc;

type Res = def::Res<ast::NodeId>;

#[derive(Clone, Debug)]
pub struct InvocationData<'a> {
    def_index: DefIndex,
    /// The module in which the macro was invoked.
    crate module: Cell<Module<'a>>,
    /// The legacy scope in which the macro was invoked.
    /// The invocation path is resolved in this scope.
    crate parent_legacy_scope: Cell<LegacyScope<'a>>,
    /// The legacy scope *produced* by expanding this macro invocation,
    /// includes all the macro_rules items, other invocations, etc generated by it.
    /// `None` if the macro is not expanded yet.
    crate output_legacy_scope: Cell<Option<LegacyScope<'a>>>,
}

impl<'a> InvocationData<'a> {
    pub fn root(graph_root: Module<'a>) -> Self {
        InvocationData {
            module: Cell::new(graph_root),
            def_index: CRATE_DEF_INDEX,
            parent_legacy_scope: Cell::new(LegacyScope::Empty),
            output_legacy_scope: Cell::new(Some(LegacyScope::Empty)),
        }
    }
}

/// Binding produced by a `macro_rules` item.
/// Not modularized, can shadow previous legacy bindings, etc.
#[derive(Debug)]
pub struct LegacyBinding<'a> {
    binding: &'a NameBinding<'a>,
    /// Legacy scope into which the `macro_rules` item was planted.
    parent_legacy_scope: LegacyScope<'a>,
    ident: Ident,
}

/// The scope introduced by a `macro_rules!` macro.
/// This starts at the macro's definition and ends at the end of the macro's parent
/// module (named or unnamed), or even further if it escapes with `#[macro_use]`.
/// Some macro invocations need to introduce legacy scopes too because they
/// can potentially expand into macro definitions.
#[derive(Copy, Clone, Debug)]
pub enum LegacyScope<'a> {
    /// Created when invocation data is allocated in the arena;
    /// must be replaced with a proper scope later.
    Uninitialized,
    /// Empty "root" scope at the crate start containing no names.
    Empty,
    /// The scope introduced by a `macro_rules!` macro definition.
    Binding(&'a LegacyBinding<'a>),
    /// The scope introduced by a macro invocation that can potentially
    /// create a `macro_rules!` macro definition.
    Invocation(&'a InvocationData<'a>),
}

/// Everything you need to resolve a macro or import path.
#[derive(Clone, Debug)]
pub struct ParentScope<'a> {
    crate module: Module<'a>,
    crate expansion: Mark,
    crate legacy: LegacyScope<'a>,
    crate derives: Vec<ast::Path>,
}

// Macro namespace is separated into two sub-namespaces, one for bang macros and
// one for attribute-like macros (attributes, derives).
// We ignore resolutions from one sub-namespace when searching names in scope for another.
fn sub_namespace_match(candidate: Option<MacroKind>, requirement: Option<MacroKind>) -> bool {
    #[derive(PartialEq)]
    enum SubNS { Bang, AttrLike }
    let sub_ns = |kind| match kind {
        MacroKind::Bang => Some(SubNS::Bang),
        MacroKind::Attr | MacroKind::Derive => Some(SubNS::AttrLike),
        MacroKind::ProcMacroStub => None,
    };
    let requirement = requirement.and_then(|kind| sub_ns(kind));
    let candidate = candidate.and_then(|kind| sub_ns(kind));
    // "No specific sub-namespace" means "matches anything" for both requirements and candidates.
    candidate.is_none() || requirement.is_none() || candidate == requirement
}

impl<'a> base::Resolver for Resolver<'a> {
    fn next_node_id(&mut self) -> ast::NodeId {
        self.session.next_node_id()
    }

    fn get_module_scope(&mut self, id: ast::NodeId) -> Mark {
        let mark = Mark::fresh(Mark::root());
        let module = self.module_map[&self.definitions.local_def_id(id)];
        self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData {
            module: Cell::new(module),
            def_index: module.def_id().unwrap().index,
            parent_legacy_scope: Cell::new(LegacyScope::Empty),
            output_legacy_scope: Cell::new(Some(LegacyScope::Empty)),
        }));
        mark
    }

    fn resolve_dollar_crates(&mut self, fragment: &AstFragment) {
        struct ResolveDollarCrates<'a, 'b: 'a> {
            resolver: &'a mut Resolver<'b>
        }
        impl<'a> Visitor<'a> for ResolveDollarCrates<'a, '_> {
            fn visit_ident(&mut self, ident: Ident) {
                if ident.name == keywords::DollarCrate.name() {
                    let name = match self.resolver.resolve_crate_root(ident).kind {
                        ModuleKind::Def(.., name) if name != keywords::Invalid.name() => name,
                        _ => keywords::Crate.name(),
                    };
                    ident.span.ctxt().set_dollar_crate_name(name);
                }
            }
            fn visit_mac(&mut self, _: &ast::Mac) {}
        }

        fragment.visit_with(&mut ResolveDollarCrates { resolver: self });
    }

    fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
                                            derives: &[Mark]) {
        let invocation = self.invocations[&mark];
        self.collect_def_ids(mark, invocation, fragment);

        self.current_module = invocation.module.get();
        self.current_module.unresolved_invocations.borrow_mut().remove(&mark);
        self.current_module.unresolved_invocations.borrow_mut().extend(derives);
        self.invocations.extend(derives.iter().map(|&derive| (derive, invocation)));
        let mut visitor = BuildReducedGraphVisitor {
            resolver: self,
            current_legacy_scope: invocation.parent_legacy_scope.get(),
            expansion: mark,
        };
        fragment.visit_with(&mut visitor);
        invocation.output_legacy_scope.set(Some(visitor.current_legacy_scope));
    }

    fn add_builtin(&mut self, ident: ast::Ident, ext: Lrc<SyntaxExtension>) {
        let def_id = DefId {
            krate: CrateNum::BuiltinMacros,
            index: DefIndex::from_array_index(self.macro_map.len()),
        };
        let kind = ext.kind();
        self.macro_map.insert(def_id, ext);
        let binding = self.arenas.alloc_name_binding(NameBinding {
            kind: NameBindingKind::Res(Res::Def(DefKind::Macro(kind), def_id), false),
            ambiguity: None,
            span: DUMMY_SP,
            vis: ty::Visibility::Public,
            expansion: Mark::root(),
        });
        if self.builtin_macros.insert(ident.name, binding).is_some() {
            self.session.span_err(ident.span,
                                  &format!("built-in macro `{}` was already defined", ident));
        }
    }

    fn resolve_imports(&mut self) {
        ImportResolver { resolver: self }.resolve_imports()
    }

    fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: Mark, force: bool)
                                -> Result<Option<Lrc<SyntaxExtension>>, Determinacy> {
        let (path, kind, derives_in_scope, after_derive) = match invoc.kind {
            InvocationKind::Attr { attr: None, .. } =>
                return Ok(None),
            InvocationKind::Attr { attr: Some(ref attr), ref traits, after_derive, .. } =>
                (&attr.path, MacroKind::Attr, traits.clone(), after_derive),
            InvocationKind::Bang { ref mac, .. } =>
                (&mac.node.path, MacroKind::Bang, Vec::new(), false),
            InvocationKind::Derive { ref path, .. } =>
                (path, MacroKind::Derive, Vec::new(), false),
        };

        let parent_scope = self.invoc_parent_scope(invoc_id, derives_in_scope);
        let (res, ext) = match self.resolve_macro_to_res(path, kind, &parent_scope, true, force) {
            Ok((res, ext)) => (res, ext),
            Err(Determinacy::Determined) if kind == MacroKind::Attr => {
                // Replace unresolved attributes with used inert attributes for better recovery.
                return Ok(Some(Lrc::new(SyntaxExtension::NonMacroAttr { mark_used: true })));
            }
            Err(determinacy) => return Err(determinacy),
        };

        if let Res::Def(DefKind::Macro(_), def_id) = res {
            if after_derive {
                self.session.span_err(invoc.span(),
                                      "macro attributes must be placed before `#[derive]`");
            }
            self.macro_defs.insert(invoc.expansion_data.mark, def_id);
            let normal_module_def_id =
                self.macro_def_scope(invoc.expansion_data.mark).normal_ancestor_id;
            self.definitions.add_parent_module_of_macro_def(invoc.expansion_data.mark,
                                                            normal_module_def_id);
            invoc.expansion_data.mark.set_default_transparency(ext.default_transparency());
        }

        Ok(Some(ext))
    }

    fn resolve_macro_path(&mut self, path: &ast::Path, kind: MacroKind, invoc_id: Mark,
                          derives_in_scope: Vec<ast::Path>, force: bool)
                          -> Result<Lrc<SyntaxExtension>, Determinacy> {
        let parent_scope = self.invoc_parent_scope(invoc_id, derives_in_scope);
        Ok(self.resolve_macro_to_res(path, kind, &parent_scope, false, force)?.1)
    }

    fn check_unused_macros(&self) {
        for did in self.unused_macros.iter() {
            let id_span = match *self.macro_map[did] {
                SyntaxExtension::NormalTT { def_info, .. } |
                SyntaxExtension::DeclMacro { def_info, .. } => def_info,
                _ => None,
            };
            if let Some((id, span)) = id_span {
                let lint = lint::builtin::UNUSED_MACROS;
                let msg = "unused macro definition";
                self.session.buffer_lint(lint, id, span, msg);
            } else {
                bug!("attempted to create unused macro error, but span not available");
            }
        }
    }
}

impl<'a> Resolver<'a> {
    pub fn dummy_parent_scope(&self) -> ParentScope<'a> {
        self.invoc_parent_scope(Mark::root(), Vec::new())
    }

    fn invoc_parent_scope(&self, invoc_id: Mark, derives: Vec<ast::Path>) -> ParentScope<'a> {
        let invoc = self.invocations[&invoc_id];
        ParentScope {
            module: invoc.module.get().nearest_item_scope(),
            expansion: invoc_id.parent(),
            legacy: invoc.parent_legacy_scope.get(),
            derives,
        }
    }

    fn resolve_macro_to_res(
        &mut self,
        path: &ast::Path,
        kind: MacroKind,
        parent_scope: &ParentScope<'a>,
        trace: bool,
        force: bool,
    ) -> Result<(Res, Lrc<SyntaxExtension>), Determinacy> {
        let res = self.resolve_macro_to_res_inner(path, kind, parent_scope, trace, force);

        // Report errors and enforce feature gates for the resolved macro.
        if res != Err(Determinacy::Undetermined) {
            // Do not report duplicated errors on every undetermined resolution.
            for segment in &path.segments {
                if let Some(args) = &segment.args {
                    self.session.span_err(args.span(), "generic arguments in macro path");
                }
            }
        }

        let res = res?;

        match res {
            Res::Def(DefKind::Macro(macro_kind), def_id) => {
                self.unused_macros.remove(&def_id);
                if macro_kind == MacroKind::ProcMacroStub {
                    let msg = "can't use a procedural macro from the same crate that defines it";
                    self.session.span_err(path.span, msg);
                    return Err(Determinacy::Determined);
                }
            }
            Res::NonMacroAttr(attr_kind) => {
                if kind == MacroKind::Attr {
                    let features = self.session.features_untracked();
                    if attr_kind == NonMacroAttrKind::Custom {
                        assert!(path.segments.len() == 1);
                        let name = path.segments[0].ident.as_str();
                        if name.starts_with("rustc_") {
                            if !features.rustc_attrs {
                                let msg = "unless otherwise specified, attributes with the prefix \
                                           `rustc_` are reserved for internal compiler diagnostics";
                                self.report_unknown_attribute(path.span, &name, msg, "rustc_attrs");
                            }
                        } else if !features.custom_attribute {
                            let msg = format!("The attribute `{}` is currently unknown to the \
                                               compiler and may have meaning added to it in the \
                                               future", path);
                            self.report_unknown_attribute(
                                path.span,
                                &name,
                                &msg,
                                "custom_attribute",
                            );
                        }
                    }
                } else {
                    // Not only attributes, but anything in macro namespace can result in
                    // `Res::NonMacroAttr` definition (e.g., `inline!()`), so we must report
                    // an error for those cases.
                    let msg = format!("expected a macro, found {}", res.descr());
                    self.session.span_err(path.span, &msg);
                    return Err(Determinacy::Determined);
                }
            }
            Res::Err => {
                return Err(Determinacy::Determined);
            }
            _ => panic!("expected `DefKind::Macro` or `Res::NonMacroAttr`"),
        }

        Ok((res, self.get_macro(res)))
    }

    fn report_unknown_attribute(&self, span: Span, name: &str, msg: &str, feature: &str) {
        let mut err = feature_err(
            &self.session.parse_sess,
            feature,
            span,
            GateIssue::Language,
            &msg,
        );

        let features = self.session.features_untracked();

        let attr_candidates = BUILTIN_ATTRIBUTES
            .iter()
            .filter_map(|&(name, _, _, ref gate)| {
                if name.as_str().starts_with("rustc_") && !features.rustc_attrs {
                    return None;
                }

                match gate {
                    AttributeGate::Gated(Stability::Unstable, ..)
                        if self.session.opts.unstable_features.is_nightly_build() =>
                    {
                        Some(name)
                    }
                    AttributeGate::Gated(Stability::Deprecated(..), ..) => Some(name),
                    AttributeGate::Ungated => Some(name),
                    _ => None,
                }
            })
            .chain(
                // Add built-in macro attributes as well.
                self.builtin_macros.iter().filter_map(|(name, binding)| {
                    match binding.macro_kind() {
                        Some(MacroKind::Attr) => Some(*name),
                        _ => None,
                    }
                }),
            )
            .collect::<Vec<_>>();

        let lev_suggestion = find_best_match_for_name(attr_candidates.iter(), &name, None);

        if let Some(suggestion) = lev_suggestion {
            err.span_suggestion(
                span,
                "a built-in attribute with a similar name exists",
                suggestion.to_string(),
                Applicability::MaybeIncorrect,
            );
        }

        err.emit();
    }

    pub fn resolve_macro_to_res_inner(
        &mut self,
        path: &ast::Path,
        kind: MacroKind,
        parent_scope: &ParentScope<'a>,
        trace: bool,
        force: bool,
    ) -> Result<Res, Determinacy> {
        let path_span = path.span;
        let mut path = Segment::from_path(path);

        // Possibly apply the macro helper hack
        if kind == MacroKind::Bang && path.len() == 1 &&
           path[0].ident.span.ctxt().outer().expn_info()
               .map_or(false, |info| info.local_inner_macros) {
            let root = Ident::new(keywords::DollarCrate.name(), path[0].ident.span);
            path.insert(0, Segment::from_ident(root));
        }

        if path.len() > 1 {
            let res = match self.resolve_path(&path, Some(MacroNS), parent_scope,
                                              false, path_span, CrateLint::No) {
                PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
                    Ok(path_res.base_res())
                }
                PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
                PathResult::NonModule(..)
                | PathResult::Indeterminate
                | PathResult::Failed { .. } => Err(Determinacy::Determined),
                PathResult::Module(..) => unreachable!(),
            };

            if trace {
                parent_scope.module.multi_segment_macro_resolutions.borrow_mut()
                    .push((path, path_span, kind, parent_scope.clone(), res.ok()));
            }

            self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span);
            res
        } else {
            let binding = self.early_resolve_ident_in_lexical_scope(
                path[0].ident, ScopeSet::Macro(kind), parent_scope, false, force, path_span
            );
            if let Err(Determinacy::Undetermined) = binding {
                return Err(Determinacy::Undetermined);
            }

            if trace {
                parent_scope.module.single_segment_macro_resolutions.borrow_mut()
                    .push((path[0].ident, kind, parent_scope.clone(), binding.ok()));
            }

            let res = binding.map(|binding| binding.res());
            self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span);
            res
        }
    }

    // Resolve an identifier in lexical scope.
    // This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during
    // expansion and import resolution (perhaps they can be merged in the future).
    // The function is used for resolving initial segments of macro paths (e.g., `foo` in
    // `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition.
    crate fn early_resolve_ident_in_lexical_scope(
        &mut self,
        orig_ident: Ident,
        scope_set: ScopeSet,
        parent_scope: &ParentScope<'a>,
        record_used: bool,
        force: bool,
        path_span: Span,
    ) -> Result<&'a NameBinding<'a>, Determinacy> {
        // General principles:
        // 1. Not controlled (user-defined) names should have higher priority than controlled names
        //    built into the language or standard library. This way we can add new names into the
        //    language or standard library without breaking user code.
        // 2. "Closed set" below means new names cannot appear after the current resolution attempt.
        // Places to search (in order of decreasing priority):
        // (Type NS)
        // 1. FIXME: Ribs (type parameters), there's no necessary infrastructure yet
        //    (open set, not controlled).
        // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
        //    (open, not controlled).
        // 3. Extern prelude (closed, not controlled).
        // 4. Tool modules (closed, controlled right now, but not in the future).
        // 5. Standard library prelude (de-facto closed, controlled).
        // 6. Language prelude (closed, controlled).
        // (Value NS)
        // 1. FIXME: Ribs (local variables), there's no necessary infrastructure yet
        //    (open set, not controlled).
        // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
        //    (open, not controlled).
        // 3. Standard library prelude (de-facto closed, controlled).
        // (Macro NS)
        // 1-3. Derive helpers (open, not controlled). All ambiguities with other names
        //    are currently reported as errors. They should be higher in priority than preludes
        //    and probably even names in modules according to the "general principles" above. They
        //    also should be subject to restricted shadowing because are effectively produced by
        //    derives (you need to resolve the derive first to add helpers into scope), but they
        //    should be available before the derive is expanded for compatibility.
        //    It's mess in general, so we are being conservative for now.
        // 1-3. `macro_rules` (open, not controlled), loop through legacy scopes. Have higher
        //    priority than prelude macros, but create ambiguities with macros in modules.
        // 1-3. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
        //    (open, not controlled). Have higher priority than prelude macros, but create
        //    ambiguities with `macro_rules`.
        // 4. `macro_use` prelude (open, the open part is from macro expansions, not controlled).
        // 4a. User-defined prelude from macro-use
        //    (open, the open part is from macro expansions, not controlled).
        // 4b. Standard library prelude is currently implemented as `macro-use` (closed, controlled)
        // 5. Language prelude: builtin macros (closed, controlled, except for legacy plugins).
        // 6. Language prelude: builtin attributes (closed, controlled).
        // 4-6. Legacy plugin helpers (open, not controlled). Similar to derive helpers,
        //    but introduced by legacy plugins using `register_attribute`. Priority is somewhere
        //    in prelude, not sure where exactly (creates ambiguities with any other prelude names).

        enum WhereToResolve<'a> {
            DeriveHelpers,
            MacroRules(LegacyScope<'a>),
            CrateRoot,
            Module(Module<'a>),
            MacroUsePrelude,
            BuiltinMacros,
            BuiltinAttrs,
            LegacyPluginHelpers,
            ExternPrelude,
            ToolPrelude,
            StdLibPrelude,
            BuiltinTypes,
        }

        bitflags::bitflags! {
            struct Flags: u8 {
                const MACRO_RULES        = 1 << 0;
                const MODULE             = 1 << 1;
                const PRELUDE            = 1 << 2;
                const MISC_SUGGEST_CRATE = 1 << 3;
                const MISC_SUGGEST_SELF  = 1 << 4;
                const MISC_FROM_PRELUDE  = 1 << 5;
            }
        }

        assert!(force || !record_used); // `record_used` implies `force`
        let mut ident = orig_ident.modern();

        // Make sure `self`, `super` etc produce an error when passed to here.
        if ident.is_path_segment_keyword() {
            return Err(Determinacy::Determined);
        }

        // This is *the* result, resolution from the scope closest to the resolved identifier.
        // However, sometimes this result is "weak" because it comes from a glob import or
        // a macro expansion, and in this case it cannot shadow names from outer scopes, e.g.
        // mod m { ... } // solution in outer scope
        // {
        //     use prefix::*; // imports another `m` - innermost solution
        //                    // weak, cannot shadow the outer `m`, need to report ambiguity error
        //     m::mac!();
        // }
        // So we have to save the innermost solution and continue searching in outer scopes
        // to detect potential ambiguities.
        let mut innermost_result: Option<(&NameBinding<'_>, Flags)> = None;

        // Go through all the scopes and try to resolve the name.
        let rust_2015 = orig_ident.span.rust_2015();
        let (ns, macro_kind, is_import, is_absolute_path) = match scope_set {
            ScopeSet::Import(ns) => (ns, None, true, false),
            ScopeSet::AbsolutePath(ns) => (ns, None, false, true),
            ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false, false),
            ScopeSet::Module => (TypeNS, None, false, false),
        };
        let mut where_to_resolve = match ns {
            _ if is_absolute_path => WhereToResolve::CrateRoot,
            TypeNS | ValueNS => WhereToResolve::Module(parent_scope.module),
            MacroNS => WhereToResolve::DeriveHelpers,
        };
        let mut use_prelude = !parent_scope.module.no_implicit_prelude;
        let mut determinacy = Determinacy::Determined;
        loop {
            let result = match where_to_resolve {
                WhereToResolve::DeriveHelpers => {
                    let mut result = Err(Determinacy::Determined);
                    for derive in &parent_scope.derives {
                        let parent_scope = ParentScope { derives: Vec::new(), ..*parent_scope };
                        match self.resolve_macro_to_res(derive, MacroKind::Derive,
                                                        &parent_scope, true, force) {
                            Ok((_, ext)) => {
                                if let SyntaxExtension::ProcMacroDerive(_, helpers, _) = &*ext {
                                    if helpers.contains(&ident.name) {
                                        let binding =
                                            (Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper),
                                            ty::Visibility::Public, derive.span, Mark::root())
                                            .to_name_binding(self.arenas);
                                        result = Ok((binding, Flags::empty()));
                                        break;
                                    }
                                }
                            }
                            Err(Determinacy::Determined) => {}
                            Err(Determinacy::Undetermined) =>
                                result = Err(Determinacy::Undetermined),
                        }
                    }
                    result
                }
                WhereToResolve::MacroRules(legacy_scope) => match legacy_scope {
                    LegacyScope::Binding(legacy_binding) if ident == legacy_binding.ident =>
                        Ok((legacy_binding.binding, Flags::MACRO_RULES)),
                    LegacyScope::Invocation(invoc) if invoc.output_legacy_scope.get().is_none() =>
                        Err(Determinacy::Undetermined),
                    _ => Err(Determinacy::Determined),
                }
                WhereToResolve::CrateRoot => {
                    let root_ident = Ident::new(keywords::PathRoot.name(), orig_ident.span);
                    let root_module = self.resolve_crate_root(root_ident);
                    let binding = self.resolve_ident_in_module_ext(
                        ModuleOrUniformRoot::Module(root_module),
                        orig_ident,
                        ns,
                        None,
                        record_used,
                        path_span,
                    );
                    match binding {
                        Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)),
                        Err((Determinacy::Undetermined, Weak::No)) =>
                            return Err(Determinacy::determined(force)),
                        Err((Determinacy::Undetermined, Weak::Yes)) =>
                            Err(Determinacy::Undetermined),
                        Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
                    }
                }
                WhereToResolve::Module(module) => {
                    let orig_current_module = mem::replace(&mut self.current_module, module);
                    let binding = self.resolve_ident_in_module_unadjusted_ext(
                        ModuleOrUniformRoot::Module(module),
                        ident,
                        ns,
                        None,
                        true,
                        record_used,
                        path_span,
                    );
                    self.current_module = orig_current_module;
                    match binding {
                        Ok(binding) => {
                            let misc_flags = if ptr::eq(module, self.graph_root) {
                                Flags::MISC_SUGGEST_CRATE
                            } else if module.is_normal() {
                                Flags::MISC_SUGGEST_SELF
                            } else {
                                Flags::empty()
                            };
                            Ok((binding, Flags::MODULE | misc_flags))
                        }
                        Err((Determinacy::Undetermined, Weak::No)) =>
                            return Err(Determinacy::determined(force)),
                        Err((Determinacy::Undetermined, Weak::Yes)) =>
                            Err(Determinacy::Undetermined),
                        Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
                    }
                }
                WhereToResolve::MacroUsePrelude => {
                    if use_prelude || rust_2015 {
                        match self.macro_use_prelude.get(&ident.name).cloned() {
                            Some(binding) =>
                                Ok((binding, Flags::PRELUDE | Flags::MISC_FROM_PRELUDE)),
                            None => Err(Determinacy::determined(
                                self.graph_root.unresolved_invocations.borrow().is_empty()
                            ))
                        }
                    } else {
                        Err(Determinacy::Determined)
                    }
                }
                WhereToResolve::BuiltinMacros => {
                    match self.builtin_macros.get(&ident.name).cloned() {
                        Some(binding) => Ok((binding, Flags::PRELUDE)),
                        None => Err(Determinacy::Determined),
                    }
                }
                WhereToResolve::BuiltinAttrs => {
                    if is_builtin_attr_name(ident.name) {
                        let binding = (Res::NonMacroAttr(NonMacroAttrKind::Builtin),
                                       ty::Visibility::Public, DUMMY_SP, Mark::root())
                                       .to_name_binding(self.arenas);
                        Ok((binding, Flags::PRELUDE))
                    } else {
                        Err(Determinacy::Determined)
                    }
                }
                WhereToResolve::LegacyPluginHelpers => {
                    if (use_prelude || rust_2015) &&
                       self.session.plugin_attributes.borrow().iter()
                                                     .any(|(name, _)| ident.name == &**name) {
                        let binding = (Res::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper),
                                       ty::Visibility::Public, DUMMY_SP, Mark::root())
                                       .to_name_binding(self.arenas);
                        Ok((binding, Flags::PRELUDE))
                    } else {
                        Err(Determinacy::Determined)
                    }
                }
                WhereToResolve::ExternPrelude => {
                    if use_prelude || is_absolute_path {
                        match self.extern_prelude_get(ident, !record_used) {
                            Some(binding) => Ok((binding, Flags::PRELUDE)),
                            None => Err(Determinacy::determined(
                                self.graph_root.unresolved_invocations.borrow().is_empty()
                            )),
                        }
                    } else {
                        Err(Determinacy::Determined)
                    }
                }
                WhereToResolve::ToolPrelude => {
                    if use_prelude && is_known_tool(ident.name) {
                        let binding = (Res::ToolMod, ty::Visibility::Public,
                                       DUMMY_SP, Mark::root()).to_name_binding(self.arenas);
                        Ok((binding, Flags::PRELUDE))
                    } else {
                        Err(Determinacy::Determined)
                    }
                }
                WhereToResolve::StdLibPrelude => {
                    let mut result = Err(Determinacy::Determined);
                    if use_prelude {
                        if let Some(prelude) = self.prelude {
                            if let Ok(binding) = self.resolve_ident_in_module_unadjusted(
                                ModuleOrUniformRoot::Module(prelude),
                                ident,
                                ns,
                                false,
                                path_span,
                            ) {
                                result = Ok((binding, Flags::PRELUDE | Flags::MISC_FROM_PRELUDE));
                            }
                        }
                    }
                    result
                }
                WhereToResolve::BuiltinTypes => {
                    match self.primitive_type_table.primitive_types.get(&ident.name).cloned() {
                        Some(prim_ty) => {
                            let binding = (Res::PrimTy(prim_ty), ty::Visibility::Public,
                                           DUMMY_SP, Mark::root()).to_name_binding(self.arenas);
                            Ok((binding, Flags::PRELUDE))
                        }
                        None => Err(Determinacy::Determined)
                    }
                }
            };

            match result {
                Ok((binding, flags)) if sub_namespace_match(binding.macro_kind(), macro_kind) => {
                    if !record_used {
                        return Ok(binding);
                    }

                    if let Some((innermost_binding, innermost_flags)) = innermost_result {
                        // Found another solution, if the first one was "weak", report an error.
                        let (res, innermost_res) = (binding.res(), innermost_binding.res());
                        if res != innermost_res {
                            let builtin = Res::NonMacroAttr(NonMacroAttrKind::Builtin);
                            let derive_helper = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
                            let legacy_helper =
                                Res::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper);

                            let ambiguity_error_kind = if is_import {
                                Some(AmbiguityKind::Import)
                            } else if innermost_res == builtin || res == builtin {
                                Some(AmbiguityKind::BuiltinAttr)
                            } else if innermost_res == derive_helper || res == derive_helper {
                                Some(AmbiguityKind::DeriveHelper)
                            } else if innermost_res == legacy_helper &&
                                      flags.contains(Flags::PRELUDE) ||
                                      res == legacy_helper &&
                                      innermost_flags.contains(Flags::PRELUDE) {
                                Some(AmbiguityKind::LegacyHelperVsPrelude)
                            } else if innermost_flags.contains(Flags::MACRO_RULES) &&
                                      flags.contains(Flags::MODULE) &&
                                      !self.disambiguate_legacy_vs_modern(innermost_binding,
                                                                          binding) ||
                                      flags.contains(Flags::MACRO_RULES) &&
                                      innermost_flags.contains(Flags::MODULE) &&
                                      !self.disambiguate_legacy_vs_modern(binding,
                                                                          innermost_binding) {
                                Some(AmbiguityKind::LegacyVsModern)
                            } else if innermost_binding.is_glob_import() {
                                Some(AmbiguityKind::GlobVsOuter)
                            } else if innermost_binding.may_appear_after(parent_scope.expansion,
                                                                         binding) {
                                Some(AmbiguityKind::MoreExpandedVsOuter)
                            } else {
                                None
                            };
                            if let Some(kind) = ambiguity_error_kind {
                                let misc = |f: Flags| if f.contains(Flags::MISC_SUGGEST_CRATE) {
                                    AmbiguityErrorMisc::SuggestCrate
                                } else if f.contains(Flags::MISC_SUGGEST_SELF) {
                                    AmbiguityErrorMisc::SuggestSelf
                                } else if f.contains(Flags::MISC_FROM_PRELUDE) {
                                    AmbiguityErrorMisc::FromPrelude
                                } else {
                                    AmbiguityErrorMisc::None
                                };
                                self.ambiguity_errors.push(AmbiguityError {
                                    kind,
                                    ident: orig_ident,
                                    b1: innermost_binding,
                                    b2: binding,
                                    misc1: misc(innermost_flags),
                                    misc2: misc(flags),
                                });
                                return Ok(innermost_binding);
                            }
                        }
                    } else {
                        // Found the first solution.
                        innermost_result = Some((binding, flags));
                    }
                }
                Ok(..) | Err(Determinacy::Determined) => {}
                Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined
            }

            where_to_resolve = match where_to_resolve {
                WhereToResolve::DeriveHelpers =>
                    WhereToResolve::MacroRules(parent_scope.legacy),
                WhereToResolve::MacroRules(legacy_scope) => match legacy_scope {
                    LegacyScope::Binding(binding) => WhereToResolve::MacroRules(
                        binding.parent_legacy_scope
                    ),
                    LegacyScope::Invocation(invoc) => WhereToResolve::MacroRules(
                        invoc.output_legacy_scope.get().unwrap_or(invoc.parent_legacy_scope.get())
                    ),
                    LegacyScope::Empty => WhereToResolve::Module(parent_scope.module),
                    LegacyScope::Uninitialized => unreachable!(),
                }
                WhereToResolve::CrateRoot => match ns {
                    TypeNS => {
                        ident.span.adjust(Mark::root());
                        WhereToResolve::ExternPrelude
                    }
                    ValueNS | MacroNS => break,
                }
                WhereToResolve::Module(module) => {
                    match self.hygienic_lexical_parent(module, &mut ident.span) {
                        Some(parent_module) => WhereToResolve::Module(parent_module),
                        None => {
                            use_prelude = !module.no_implicit_prelude;
                            match ns {
                                TypeNS => WhereToResolve::ExternPrelude,
                                ValueNS => WhereToResolve::StdLibPrelude,
                                MacroNS => WhereToResolve::MacroUsePrelude,
                            }
                        }
                    }
                }
                WhereToResolve::MacroUsePrelude => WhereToResolve::BuiltinMacros,
                WhereToResolve::BuiltinMacros => WhereToResolve::BuiltinAttrs,
                WhereToResolve::BuiltinAttrs => WhereToResolve::LegacyPluginHelpers,
                WhereToResolve::LegacyPluginHelpers => break, // nowhere else to search
                WhereToResolve::ExternPrelude if is_absolute_path => break,
                WhereToResolve::ExternPrelude => WhereToResolve::ToolPrelude,
                WhereToResolve::ToolPrelude => WhereToResolve::StdLibPrelude,
                WhereToResolve::StdLibPrelude => match ns {
                    TypeNS => WhereToResolve::BuiltinTypes,
                    ValueNS => break, // nowhere else to search
                    MacroNS => unreachable!(),
                }
                WhereToResolve::BuiltinTypes => break, // nowhere else to search
            };

            continue;
        }

        // The first found solution was the only one, return it.
        if let Some((binding, _)) = innermost_result {
            return Ok(binding);
        }

        let determinacy = Determinacy::determined(determinacy == Determinacy::Determined || force);
        if determinacy == Determinacy::Determined && macro_kind == Some(MacroKind::Attr) {
            // For single-segment attributes interpret determinate "no resolution" as a custom
            // attribute. (Lexical resolution implies the first segment and attr kind should imply
            // the last segment, so we are certainly working with a single-segment attribute here.)
            assert!(ns == MacroNS);
            let binding = (Res::NonMacroAttr(NonMacroAttrKind::Custom),
                           ty::Visibility::Public, ident.span, Mark::root())
                           .to_name_binding(self.arenas);
            Ok(binding)
        } else {
            Err(determinacy)
        }
    }

    pub fn finalize_current_module_macro_resolutions(&mut self) {
        let module = self.current_module;

        let check_consistency = |this: &mut Self, path: &[Segment], span, kind: MacroKind,
                                 initial_res: Option<Res>, res: Res| {
            if let Some(initial_res) = initial_res {
                if res != initial_res && res != Res::Err && this.ambiguity_errors.is_empty() {
                    // Make sure compilation does not succeed if preferred macro resolution
                    // has changed after the macro had been expanded. In theory all such
                    // situations should be reported as ambiguity errors, so this is a bug.
                    if initial_res == Res::NonMacroAttr(NonMacroAttrKind::Custom) {
                        // Yeah, legacy custom attributes are implemented using forced resolution
                        // (which is a best effort error recovery tool, basically), so we can't
                        // promise their resolution won't change later.
                        let msg = format!("inconsistent resolution for a macro: first {}, then {}",
                                          initial_res.descr(), res.descr());
                        this.session.span_err(span, &msg);
                    } else {
                        span_bug!(span, "inconsistent resolution for a macro");
                    }
                }
            } else {
                // It's possible that the macro was unresolved (indeterminate) and silently
                // expanded into a dummy fragment for recovery during expansion.
                // Now, post-expansion, the resolution may succeed, but we can't change the
                // past and need to report an error.
                // However, non-speculative `resolve_path` can successfully return private items
                // even if speculative `resolve_path` returned nothing previously, so we skip this
                // less informative error if the privacy error is reported elsewhere.
                if this.privacy_errors.is_empty() {
                    let msg = format!("cannot determine resolution for the {} `{}`",
                                        kind.descr(), Segment::names_to_string(path));
                    let msg_note = "import resolution is stuck, try simplifying macro imports";
                    this.session.struct_span_err(span, &msg).note(msg_note).emit();
                }
            }
        };

        let macro_resolutions =
            mem::replace(&mut *module.multi_segment_macro_resolutions.borrow_mut(), Vec::new());
        for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions {
            // FIXME: Path resolution will ICE if segment IDs present.
            for seg in &mut path { seg.id = None; }
            match self.resolve_path(&path, Some(MacroNS), &parent_scope,
                                    true, path_span, CrateLint::No) {
                PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
                    let res = path_res.base_res();
                    check_consistency(self, &path, path_span, kind, initial_res, res);
                }
                path_res @ PathResult::NonModule(..) | path_res @ PathResult::Failed { .. } => {
                    let (span, label) = if let PathResult::Failed { span, label, .. } = path_res {
                        (span, label)
                    } else {
                        (path_span, format!("partially resolved path in {} {}",
                                            kind.article(), kind.descr()))
                    };
                    resolve_error(self, span, ResolutionError::FailedToResolve {
                        label,
                        suggestion: None
                    });
                }
                PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
            }
        }

        let macro_resolutions =
            mem::replace(&mut *module.single_segment_macro_resolutions.borrow_mut(), Vec::new());
        for (ident, kind, parent_scope, initial_binding) in macro_resolutions {
            match self.early_resolve_ident_in_lexical_scope(ident, ScopeSet::Macro(kind),
                                                            &parent_scope, true, true, ident.span) {
                Ok(binding) => {
                    let initial_res = initial_binding.map(|initial_binding| {
                        self.record_use(ident, MacroNS, initial_binding, false);
                        initial_binding.res()
                    });
                    let res = binding.res();
                    let seg = Segment::from_ident(ident);
                    check_consistency(self, &[seg], ident.span, kind, initial_res, res);
                }
                Err(..) => {
                    assert!(initial_binding.is_none());
                    let bang = if kind == MacroKind::Bang { "!" } else { "" };
                    let msg =
                        format!("cannot find {} `{}{}` in this scope", kind.descr(), ident, bang);
                    let mut err = self.session.struct_span_err(ident.span, &msg);
                    self.suggest_macro_name(&ident.as_str(), kind, &mut err, ident.span);
                    err.emit();
                }
            }
        }

        let builtin_attrs = mem::replace(&mut *module.builtin_attrs.borrow_mut(), Vec::new());
        for (ident, parent_scope) in builtin_attrs {
            let _ = self.early_resolve_ident_in_lexical_scope(
                ident, ScopeSet::Macro(MacroKind::Attr), &parent_scope, true, true, ident.span
            );
        }
    }

    fn prohibit_imported_non_macro_attrs(&self, binding: Option<&'a NameBinding<'a>>,
                                         res: Option<Res>, span: Span) {
        if let Some(Res::NonMacroAttr(kind)) = res {
            if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) {
                let msg = format!("cannot use a {} through an import", kind.descr());
                let mut err = self.session.struct_span_err(span, &msg);
                if let Some(binding) = binding {
                    err.span_note(binding.span, &format!("the {} imported here", kind.descr()));
                }
                err.emit();
            }
        }
    }

    fn suggest_macro_name(&mut self, name: &str, kind: MacroKind,
                          err: &mut DiagnosticBuilder<'a>, span: Span) {
        // First check if this is a locally-defined bang macro.
        let suggestion = if let MacroKind::Bang = kind {
            find_best_match_for_name(self.macro_names.iter().map(|ident| &ident.name), name, None)
        } else {
            None
        // Then check global macros.
        }.or_else(|| {
            let names = self.builtin_macros.iter().chain(self.macro_use_prelude.iter())
                                                  .filter_map(|(name, binding)| {
                if binding.macro_kind() == Some(kind) { Some(name) } else { None }
            });
            find_best_match_for_name(names, name, None)
        // Then check modules.
        }).or_else(|| {
            let is_macro = |res| {
                if let Res::Def(DefKind::Macro(def_kind), _) = res {
                    def_kind == kind
                } else {
                    false
                }
            };
            let ident = Ident::new(Symbol::intern(name), span);
            self.lookup_typo_candidate(&[Segment::from_ident(ident)], MacroNS, is_macro, span)
                .map(|suggestion| suggestion.candidate)
        });

        if let Some(suggestion) = suggestion {
            if suggestion != name {
                if let MacroKind::Bang = kind {
                    err.span_suggestion(
                        span,
                        "you could try the macro",
                        suggestion.to_string(),
                        Applicability::MaybeIncorrect
                    );
                } else {
                    err.span_suggestion(
                        span,
                        "try",
                        suggestion.to_string(),
                        Applicability::MaybeIncorrect
                    );
                }
            } else {
                err.help("have you added the `#[macro_use]` on the module/import?");
            }
        }
    }

    fn collect_def_ids(&mut self,
                       mark: Mark,
                       invocation: &'a InvocationData<'a>,
                       fragment: &AstFragment) {
        let Resolver { ref mut invocations, arenas, graph_root, .. } = *self;
        let InvocationData { def_index, .. } = *invocation;

        let visit_macro_invoc = &mut |invoc: map::MacroInvocationData| {
            invocations.entry(invoc.mark).or_insert_with(|| {
                arenas.alloc_invocation_data(InvocationData {
                    def_index: invoc.def_index,
                    module: Cell::new(graph_root),
                    parent_legacy_scope: Cell::new(LegacyScope::Uninitialized),
                    output_legacy_scope: Cell::new(None),
                })
            });
        };

        let mut def_collector = DefCollector::new(&mut self.definitions, mark);
        def_collector.visit_macro_invoc = Some(visit_macro_invoc);
        def_collector.with_parent(def_index, |def_collector| {
            fragment.visit_with(def_collector)
        });
    }

    pub fn define_macro(&mut self,
                        item: &ast::Item,
                        expansion: Mark,
                        current_legacy_scope: &mut LegacyScope<'a>) {
        self.local_macro_def_scopes.insert(item.id, self.current_module);
        let ident = item.ident;
        if ident.name == "macro_rules" {
            self.session.span_err(item.span, "user-defined macros may not be named `macro_rules`");
        }

        let def_id = self.definitions.local_def_id(item.id);
        let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
                                               &self.session.features_untracked(),
                                               item, hygiene::default_edition()));
        self.macro_map.insert(def_id, ext);

        let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() };
        if def.legacy {
            let ident = ident.modern();
            self.macro_names.insert(ident);
            let res = Res::Def(DefKind::Macro(MacroKind::Bang), def_id);
            let is_macro_export = attr::contains_name(&item.attrs, "macro_export");
            let vis = if is_macro_export {
                ty::Visibility::Public
            } else {
                ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))
            };
            let binding = (res, vis, item.span, expansion).to_name_binding(self.arenas);
            self.set_binding_parent_module(binding, self.current_module);
            let legacy_binding = self.arenas.alloc_legacy_binding(LegacyBinding {
                parent_legacy_scope: *current_legacy_scope, binding, ident
            });
            *current_legacy_scope = LegacyScope::Binding(legacy_binding);
            self.all_macros.insert(ident.name, res);
            if is_macro_export {
                let module = self.graph_root;
                self.define(module, ident, MacroNS,
                            (res, vis, item.span, expansion, IsMacroExport));
            } else {
                if !attr::contains_name(&item.attrs, "rustc_doc_only_macro") {
                    self.check_reserved_macro_name(ident, MacroNS);
                }
                self.unused_macros.insert(def_id);
            }
        } else {
            let module = self.current_module;
            let res = Res::Def(DefKind::Macro(MacroKind::Bang), def_id);
            let vis = self.resolve_visibility(&item.vis);
            if vis != ty::Visibility::Public {
                self.unused_macros.insert(def_id);
            }
            self.define(module, ident, MacroNS, (res, vis, item.span, expansion));
        }
    }
}
